package model;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * This class holds all the data about a particular Job.
 * 
 * @author Ken Norton
 *
 * James Gunter created the original empty class.
 */
public class Job {

  /*
   * =====================================================
   * Instance fields
   * =====================================================
   */
  
  /**
   * The descriptive title for the Job.
   * 
   * invariant: Not null.
   */
  private String my_title;
  
  /**
   * A full text description of the Job.
   * 
   * invariant: Not null.
   */
  private String my_description;

  /**
   * The date that the Job will begin.
   * 
   * invariant: Not null.
   */
  private Date my_date;

  /**
   * The duration of the Job in days.
   * 
   * invariant: >= 0
   */
  private int my_duration;

  /**
   * The Park that the Job will take place at.
   * 
   * invariant: not null
   */
  private Park my_park;

  /**
   * The number of light workers needed.
   * 
   * invariant: >= 0
   */
  private int my_light_work_needed;

  /**
   * The number of light worker positions filled.
   * 
   * invariant: >= 0
   */
  private int my_light_work_filled;

  /**
   * The description of light work for this Job.
   * 
   * invariant: Not null.
   */
  private String my_light_work_description;

  /**
   * The number of medium workers needed.
   * 
   * invariant: >= 0
   */
  private int my_medium_work_needed;

  /**
   * The number of medium worker positions filled.
   * 
   * invariant: >= 0
   */
  private int my_medium_work_filled;

  /**
   * The description of medium work for this Job.
   * 
   * invariant: Not null.
   */
  private String my_medium_work_description;

  /**
   * The number of heavy workers needed.
   * 
   * invariant: >= 0
   */
  private int my_heavy_work_needed;

  /**
   * The number of heavy work positions filled.
   * 
   * invariant: >= 0
   */
  private int my_heavy_work_filled;

  /**
   * The description of heavy work for this Job.
   * 
   * invariant: Not null.
   */
  private String my_heavy_work_description;
  
  /**
   * The List of Users who are Volunteers.
   * 
   * invariant: not null
   */
  private Map<User, WorkLoad> my_volunteer_map;
  
  /**
   * Constructs a new Job object; no args.
   * 
   * pre: none
   * 
   * post: my_title is non-null.
   * post: my_light_work_needed >= 0
   * post: my_medium_work_needed >= 0
   * post: my_heavy_work_needed >= 0
   * post: my_light_work_filled >= 0
   * post: my_medium_work_filled >= 0
   * post: my_heavy_work_filled >= 0
   * post: my_park is a non-null.
   * post: my_duration
   * post: my_volunteer_list is non-null, empty 
   *       Map<User, WorkLoad>.
   * post: my_date is non-null.
   * post: my_light_work_description is non-null
   * post: my_medium_work_description is non-null
   * post: my_heavy_work_description is non-null
   * post: my_description is non-null;
   */
  public Job() {
    my_title = "";
    my_light_work_needed = 0;
    my_medium_work_needed = 0;
    my_heavy_work_needed = 0;
    my_light_work_filled = 0;
    my_medium_work_filled = 0;
    my_heavy_work_filled = 0;
    my_park = new Park();
    my_duration = 1;
    my_volunteer_map = new HashMap<User, WorkLoad>();
    my_date = new Date();
    my_light_work_description = "";
    my_medium_work_description = "";
    my_heavy_work_description = "";
    my_description = "";
  }
  
  /**
   * Constructs a new Job object; all fields.
   * 
   * @param the_desc The description of the Job.
   * @param the_date The Date the Job starts.
   * @param the_duration The duration of the Job.
   * @param the_park The Park where the Job is.
   * @param the_lt_needed The number of light needed.
   * @param the_lt_filled The number of light filled.
   * @param the_lt_description Description of light work.
   * @param the_md_needed The number of medium needed.
   * @param the_md_filled The number of medium filled.
   * @param the_md_description The description of work.
   * @param the_hv_needed The number of heavy needed.
   * @param the_hv_filled The number of heavy filled.
   * @param the_hv_description The description of work.
   * 
   * pre: my_description no pre condition
   * pre: my_date non-null
   * pre: my_duration >= 0
   * pre: my_park is non-null
   * pre: my_light_work_needed >= 0
   * pre: my_light_work_filled >= 0
   * pre: my_light_work_description has no precondition.
   * pre: my_medium_work_needed >= 0
   * pre: my_medium_work_filled >= 0
   * pre: my_medium_work_description has no precondition.
   * pre: my_heavy_work_needed >= 0
   * pre: my_heavy_work_filled >= 0
   * pre: my_heavy_work_description has no precondition.
   * 
   * post: none
   */
  public Job(final String the_title, final String the_desc, 
      final Date the_date, final int the_duration, 
      final Park the_park, final int the_lt_needed, 
      final int the_lt_filled, 
      final String the_lt_description, 
      final int the_md_needed, final int the_md_filled, 
      final String the_md_description, 
      final int the_hv_needed,
      final int the_hv_filled, 
      final String the_hv_description) {
    
    my_title = the_title;
    my_description = the_desc;
    my_date = the_date;
    my_duration = the_duration;
    my_park = the_park;
    my_light_work_needed = the_lt_needed;
    my_light_work_filled = the_lt_filled;
    my_light_work_description = the_lt_description;
    my_medium_work_needed = the_md_needed;
    my_medium_work_filled = the_md_filled;
    my_medium_work_description = the_md_description;
    my_heavy_work_needed = the_hv_needed;
    my_heavy_work_filled = the_hv_filled;
    my_heavy_work_description = the_hv_description;
    
    my_volunteer_map = new HashMap<User, WorkLoad>();
  }
  
  /*
   * =====================================================
   * Instance Methods
   * =====================================================
   */

  /**
   * Gets a complete List of Users with the Volunteer Role 
   * for this Job.
   * 
   * @return A Map<User, WorkLoad> with Volunteers for this 
   * Job.
   * 
   * pre:  none
   * 
   * post: no modification, returns non-null 
   * Map<User, WorkLoad>.
   */
  public Map<User, WorkLoad> getVolunteers() {
    return my_volunteer_map;
  }
  
  /**
   * Adds the volunteer to the List<User> for this Job.
   * 
   * @param the_volunteer The User to be added to the List.
   * 
   * pre:  non-null User with the Role Volunteer.
   * pre:  non-null WorkLoad.
   * 
   * post: my_volunteer_map.keySet().size() += 1
   * post: my_volunteer_map.get(the_volunteer) returns
   *       the_workload.
   * post: The field for the given WorkLoad is incremented.
   */
  public void addVolunteer(final User the_volunteer, 
      final WorkLoad the_workload) {
    my_volunteer_map.put(the_volunteer, the_workload);
    if (the_workload == WorkLoad.LIGHT) {
      my_light_work_filled++;
    } else if (the_workload == WorkLoad.MEDIUM) {
      my_medium_work_filled++;
    } else {
      my_heavy_work_filled++;
    }
  }
  
  /**
   * Removes the volunteer from the list.
   * 
   * @param the_volunteer
   * 
   * pre:  the_volunteer is non-null user with the Role
   *       Volunteer.
   *       
   * post: my_volunteer_map.keySet().size() -= 1
   * post: my_volunteer_map.get(the_volunteer) returns
   *       null.
   * post: The field for the WorKLoad associated with the 
   * volunteer being removed is decremented.
   *       
   */
  public void removeVolunteer(final User the_volunteer) {
    WorkLoad wl = my_volunteer_map.get(the_volunteer);
    
    if (wl == WorkLoad.LIGHT) {
      my_light_work_filled -= 1;
    } else if (wl == WorkLoad.MEDIUM) {
      my_medium_work_filled -= 1;
    } else {
      my_heavy_work_filled -= 1;
    }

    my_volunteer_map.remove(the_volunteer);
  }
  
  /**
   * Returns true if the object passed is equal to this Job.
   * 
   * @param 
   * @return A boolean.
   * 
   * pre:  none
   * 
   * post: no modifications to class data
   */
  @Override
  public boolean equals(final Object the_other) {
    boolean is_equal = false;
    if (the_other != null && 
        the_other.getClass() == getClass()) {
      Job other = (Job) the_other;
      is_equal = my_date.equals(other.getDate());
      is_equal = (is_equal && my_title.
          equals(other.getTitle()));
      is_equal = (is_equal && my_park.
          equals(other.getPark()));
      is_equal = (is_equal && my_duration == 
          other.getDuration());
    }
    return is_equal;
  }
  
  /**
   * Returns the hash code for this Job.
   * 
   * @return An int with the hashcode.
   * 
   * pre:  none
   * 
   * post: none
   */
  @Override
  public int hashCode() {
    return toString().hashCode();
  }
  
  /**
   * Generates the String representation of this Job.
   * 
   * @return The String representation of this Job.
   * 
   * pre:  none
   * 
   * post: Returns non-null string
   */
  @Override
  public String toString() {
    StringBuilder str = new StringBuilder();
    str.append(my_park.toString());
    str.append("\n");
    str.append(my_date.toString());
    str.append("\n");
    str.append(Integer.toString(my_duration));
    str.append("\n");
    str.append(my_description);
    str.append("\n");
    str.append(my_title);
    return str.toString();
  }

  /*
   * =====================================================
   * Accessors and mutators
   * =====================================================
   */

  /**
   * Sets the title of the Job.
   * 
   * @param the_title A String containing the 
   * title of the Job.
   * 
   * pre:  non-null String.
   * 
   * post: my_title is set to the_title
   */
  public void setTitle(final String the_title) {
    my_title = the_title;
  }

  /**
   * Gets the title of the Job.
   * 
   * @return A string containing the title of the Job.
   * 
   * pre: none
   * 
   * post: No modifications
   * post: Returns a non-null String.
   */
  public String getTitle() {
    return my_title;
  }
  
  /**
   * Sets the text description of the Job.
   * 
   * @param the_description A String containing the 
   * description of the Job.
   * 
   * pre:  non-null String.
   * 
   * post: my_description is set to the_description.
   */
  public void setDescription(final String the_description) {
    my_description = the_description;
  }

  /**
   * Gets the text description of the Job.
   * 
   * @return A string containing the description of the Job.
   * 
   * pre:  none
   * 
   * post: No modifications
   * post: Returns a non-null string.
   */
  public String getDescription() {
    return my_description;
  }

  /**
   * Sets the start Date of the Job.
   * 
   * @param the_date The Date that the Job will start.
   * 
   * pre:  the_date is non-null
   * 
   * pre:  the_date is >= today
   * 
   * post: my_date is non-null and = the_date.
   */
  public void setDate(final Date the_date) {
    my_date = the_date;
  }

  /**
   * Gets the start Date of the Job.
   * 
   * @return The start Date of the Job.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   * post: Returns a non-null Date object.
   */
  public Date getDate() {
    return my_date;
  }

  /**
   * Sets the duration in days of the Job.
   * 
   * @param the_duration An int for the duration of the 
   * Job in days.
   * 
   * pre: the_duration >= 0.
   * pre: the_duration <= 2.
   */
  public void setDuration(final int the_duration) {
    my_duration = the_duration;
  }

  /**
   * Gets the duration in days of the Job.
   * 
   * @return Returns an int with the duration of the Job.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   * post: Returns >= 0.
   */
  public int getDuration() {
    return my_duration;
  }

  /**
   * Sets the Park that the Job is at.
   * 
   * @param the_park The Park that the job is at.
   * 
   * pre:  the_park is non-null.
   * 
   * post: my_park is set to the_park.
   */
  public void setPark(final Park the_park) {
    my_park = the_park;
  }

  /**
   * Gets the Park that the Job is at.
   * 
   * @return Returns the Park that the Job is at.
   * 
   * pre: none.
   * 
   * post: No modifications.
   * post: Returns non-null Park object.
   */
  public Park getPark() {
    return my_park;
  }

  /**
   * Sets the number of Volunteers needed to perform 
   * light work.
   * 
   * @param the_needed The number of voluteers needed for 
   * light work.
   * 
   * pre: the_needed >= 0
   * 
   * post: my_light_work_needed is set to the_needed.
   */
  public void setLightWorkNeeded(final int the_needed) {
    my_light_work_needed = the_needed;
  }

  /**
   * Gets the number of volunteers needed to perform light 
   * work.
   * 
   * @return Returns an int with the number of volunteers 
   * needed for light work.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getLightWorkNeeded() {
    return my_light_work_needed;
  }

  /**
   * Set the number of light work positions filled.
   * 
   * @param the_filled An int with the number of work 
   * positions filled.
   * 
   * pre:  the_filled >= 0.
   * pre:  the_filled <= my_light_work_needed.
   * 
   * post: my_light_work_filled is set to the_filled.
   */
  public void setLightWorkFilled(final int the_filled) {
    my_light_work_filled = the_filled;
  }

  /**
   * Gets the number of light work position filled.
   * 
   * @return An int with the number of positions filled.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getLightWorkFilled() {
    return my_light_work_filled;
  }

  /**
   * Sets the text description of the Job.
   * 
   * @param the_description A string with the text 
   * description of the job.
   * 
   * pre:  the_description is a non-null String.
   * 
   * post: my_light_work_description is set to 
   * the_description.
   */
  public void setLtWorkDesc(final String the_description) {
    my_light_work_description = the_description;
  }

  /**
   * Gets the text description of light work.
   * 
   * @return A String containing the text description of 
   * light work.
   * 
   * pre: none.
   * 
   * post: No modifications.
   * post: Returns a non-null String.
   */
  public String getLightWorkDescription() {
    return my_light_work_description;
  }

  /**
   * Sets the number of Volunteers needed to perform medium 
   * work.
   * 
   * @param the_needed The number of voluteers needed for 
   * medium work.
   * 
   * pre:  the_needed >= 0.
   * 
   * post: my_medium_work_needed is set to the_needed.
   */
  public void setMediumWorkNeeded(final int the_needed) {
    my_medium_work_needed = the_needed;
  }

  /**
   * Gets the number of volunteers needed to perform medium 
   * work.
   * 
   * @return Returns an int with the number of volunteers 
   * needed for medium work.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getMediumWorkNeeded() {
    return my_medium_work_needed;
  }

  /**
   * Set the number of medium work positions filled.
   * 
   * @param the_filled An int with the number of work 
   * positions filled.
   * 
   * pre: the_filled >= 0
   * pre: the_filled <= my_medium_work_needed.
   * 
   * post: my_medium_work_filled is set to the_filled.
   */
  public void setMediumWorkFilled(final int the_filled) {
    my_medium_work_filled = the_filled;
  }

  /**
   * Gets the number of medium work position filled.
   * 
   * @return An int with the number of positions filled.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getMediumWorkFilled() {
    return my_medium_work_filled;
  }

  /**
   * Sets the text description of the Job.
   * 
   * @param the_description A string with the text 
   * description of the job.
   * 
   * pre: the_description is non-null.
   * 
   * post: my_medium_work_description is set to 
   * the_description.
   */
  public void setMdWorkDesc(final String the_description) {
    my_medium_work_description = the_description;
  }

  /**
   * Gets the text description of the Job.
   * 
   * @return A String containing the text description of 
   * the Job.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   * post: Returns a non-null String.
   */
  public String getMediumWorkDescription() {
    return my_medium_work_description;
  }

  /**
   * Sets the number of Volunteers needed to perform 
   * heavy work.
   * 
   * @param the_needed The number of voluteers needed for 
   * heavy work.
   * 
   * pre: the_needed >= 0
   * 
   * post: my_heavy_work_needed is set to the_needed.
   */
  public void setHeavyWorkNeeded(final int the_needed) {
    my_heavy_work_needed = the_needed;
  }

  /**
   * Gets the number of volunteers needed to perform 
   * heavy work.
   * 
   * @return Returns an int with the number of volunteers 
   * needed for heavy work.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getHeavyWorkNeeded() {
    return my_heavy_work_needed;
  }

  /**
   * Set the number of heavy work positions filled.
   * 
   * @param the_filled An int with the number of work 
   * positions filled.
   * 
   * pre:  the_filled >= 0.
   * pre:  the_filled <= my_heavy_work_filled.
   */
  public void setHeavyWorkFilled(final int the_filled) {
    my_heavy_work_filled = the_filled;
  }

  /**
   * Gets the number of heavy work position filled.
   * 
   * @return An int with the number of positions filled.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getHeavyWorkFilled() {
    return my_heavy_work_filled;
  }

  /**
   * Sets the text description of the Job.
   * 
   * @param the_description A string with the text 
   * description of the job.
   * 
   * pre: the_description is a non-null String.
   * 
   * post: my_heavy_work_description is set to
   * the_description.
   */
  public void setHvWorkDesc(final String the_description) {
    my_heavy_work_description = the_description;
  }

  /**
   * Gets the text description of the Job.
   * 
   * @return A String containing the text description of 
   * the Job.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   * post: Returns a non-null string.
   */
  public String getHeavyWorkDescription() {
    return my_heavy_work_description;
  }
  
  /**
   * Gets the number of workers needed for all work
   * categories.
   * 
   * @return The number of workers needed for all work 
   * categories.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public int getTotalNeeded() {
    return my_light_work_needed + my_medium_work_needed +
        my_heavy_work_needed;
  }
  
  /**
   * Gets the number of workers currently signed up for all
   * categories.
   * 
   * @return The number of workers currently signed up for 
   * all categories.
   * 
   * pre: none.
   * 
   * post: No modifications.
   */
  public int getTotalFilled() {
    return my_light_work_filled + my_medium_work_filled +
        my_heavy_work_filled;
  }
  
  /**
   * Returns true the total worker need is filled.
   * 
   * @return Indicator of whether the total worker need is
   * filled.
   * 
   * pre: none.
   * 
   * post: No modificatons.
   */
  public boolean isFilled() {
    return getTotalFilled() >= getTotalNeeded();
  }
  
  /**
   * Returns a true if the field for the number of 
   * workers signed up is equal to the number needed.
   * 
   * @param the_workload The WorkLoad category.
   * @return True if the WorkLoad is filled.
   * 
   * pre:  none.
   * 
   * post: No modifications.
   */
  public boolean isFilled(final WorkLoad the_workload) {
    boolean result = false;
    
    if (the_workload == WorkLoad.LIGHT) {
      result = my_light_work_filled >= my_light_work_needed;
    } else if (the_workload == WorkLoad.MEDIUM) {
      result = my_medium_work_filled >= 
          my_medium_work_needed;
    } else {
      result = my_heavy_work_filled >= my_heavy_work_needed;
    }
    return result;
  }

  /**
   * Returns true if the given user is in the list of
   * Volunteers.
   * 
   * @param the_user A Volunteer.
   * @return True if the User is in the list.
   * 
   * pre:  the_user is non-null User.
   * 
   * post: No modifications.
   */
  public boolean hasVolunteer(User the_user) {
    boolean found = false;
    if(my_volunteer_map.get(the_user) != null){
      found = true;
    }
    return found;
  }
} 
